package com.dg.acb.utils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ObjectProxy {
    private final Class<?> mClass;
    private final Object mObject;
    private final Map<String, MethodInvoker> mMethodInvokers = new HashMap<String, MethodInvoker>();

    public ObjectProxy(Class<?> class1, Object object) {
        this.mClass = class1;
        this.mObject = object;
    }

    @SuppressWarnings("unchecked")
    public <T> T getFieldValue(String name) throws ObjectProxyException {
        try {
            Field field = mClass.getDeclaredField(name);
            field.setAccessible(true);
            return (T) field.get(mObject);
        } catch (NoSuchFieldException e) {
            throw new ObjectProxyException(e);
        } catch (IllegalAccessException e) {
            throw new ObjectProxyException(e);
        } catch (IllegalArgumentException e) {
            throw new ObjectProxyException(e);
        }
    }

    public void setFieldValue(String name, Object value) throws ObjectProxyException {
        try {
            Field field = mClass.getDeclaredField(name);
            field.setAccessible(true);
            field.set(mObject, value);
        } catch (NoSuchFieldException e) {
            throw new ObjectProxyException(e);
        } catch (IllegalAccessException e) {
            throw new ObjectProxyException(e);
        } catch (IllegalArgumentException e) {
            throw new ObjectProxyException(e);
        }
    }

    public MethodInvoker getMethodInvoker(String name, Class<?>... parameterTypes) throws ObjectProxyException {
        StringBuilder keyBuilder = new StringBuilder(name);
        if (parameterTypes != null) {
            for (Class<?> class1 : parameterTypes) {
                keyBuilder.append(class1.hashCode());
            }
        }
        String methodKey = keyBuilder.toString();
        MethodInvoker methodInvoker = mMethodInvokers.get(methodKey);
        if (methodInvoker == null) {
            try {
                methodInvoker = new MethodInvoker(mObject,
                        mClass.getDeclaredMethod(name, parameterTypes));
            } catch (NoSuchMethodException e) {
                throw new ObjectProxyException(e);
            }
            mMethodInvokers.put(methodKey, methodInvoker);
        }
        return methodInvoker;
    }

    public static class MethodInvoker {
        private final Object mObject;
        public final Method mMethod;

        public MethodInvoker(Object object, Method method) {
            this.mObject = object;
            this.mMethod = method;
            if (this.mMethod != null) {
                this.mMethod.setAccessible(true);
            }
        }

        @SuppressWarnings("unchecked")
        public <T> T invoke(Object... args) throws ObjectProxyException {
            if (this.mMethod != null) {
                try {
                    return (T) this.mMethod.invoke(mObject, args);
                } catch (IllegalAccessException e) {
                    throw new ObjectProxyException(e);
                } catch (InvocationTargetException e) {
                    throw new ObjectProxyException(e);
                }
            }
            return null;
        }
    }

    public static class ObjectProxyException extends Exception {
        private static final long serialVersionUID = 1L;
        public ObjectProxyException(Throwable throwable) {
            super(throwable);
        }
    }
}
